package top.ninwoo.utils.service.impl;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import top.ninwoo.utils.entity.BridgePort;
import top.ninwoo.utils.entity.Ovs;
import top.ninwoo.utils.entity.OvsBridge;
import top.ninwoo.utils.service.OVSService;
import top.ninwoo.utils.util.LinuxCtlUtils;
import top.ninwoo.utils.util.OvsUtils;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * TODO: ovs的功能还需要再总结
 * @description 拟采用带状态的实现方案，采用单独线程进行异步更新，无论外部请求多少
 *              始终限制在一个用户去查询
 * @author joliu
 */
@Service
public class OVSServiceImpl implements OVSService, InitializingBean {
    private final static Logger LOG = LoggerFactory.getLogger(OVSServiceImpl.class);

    @Autowired
    private OvsUtils ovsUtils;

    // TODO: 感觉应该把这个OVS类拆分开
    private ConcurrentHashMap<String, OvsBridge> ovsBridges = new ConcurrentHashMap<>();
    // 是否安装了ovs
    private boolean isInstall = false;
    // ovs版本号
    private String ovsVersion;
    private String ovsId;

    @Override
    public void afterPropertiesSet() throws Exception {
        fullUpdate();
    }

    /**
     * 全量更新
     */
    public void fullUpdate() {
        isInstall = ovsUtils.isInstall();

        if(!isInstall) {
            throw new RuntimeException("Ovs is not install properly!");
        }
        // 更新ovsBridge
        Ovs ovs = ovsUtils.showDetails();
        for (OvsBridge bridge : ovs.getBridges()) {
            ovsBridges.put(bridge.getBridgeId(), bridge);
        }

        ovsVersion = ovs.getOvsVersion();
        ovsId = ovs.getId();
    }

    /**
     * 定时更新Ovs状态
     */
    @Scheduled(fixedRate = 500)
    public void updateOvsStatus() {
        Ovs ovs = ovsUtils.showDetails();
        // 第一次检查，判断Bridge列表是否减少
        if(!ovs.getId().equals(ovsId)) {
            // 清空原有的配置，进行一次全量更新
            fullUpdate();
        }
        // 第二次检查，判断是否有新的bridge添加
        List<OvsBridge> bridges = ovs.getBridges();
        Set<String> updateList = new HashSet<>();
        bridges.forEach(b -> {
            if(!ovsBridges.containsKey(b.getBridgeId())) {
                ovsBridges.put(b.getBridgeId(), b);
                updateList.add(b.getBridgeId());
            } else if(ovsBridges.containsKey(b.getBridgeId())
                    && !ovsBridges.get(b.getBridgeId()).equals(b)) {
                ovsBridges.put(b.getBridgeId(), b);
                updateList.add(b.getBridgeId());
            } else if(ovsBridges.containsKey(b.getBridgeId())
                    && ovsBridges.get(b.getBridgeId()).equals(b)) {
                updateList.add(b.getBridgeId());
            }
        });
        // 删除丢失的点
        ovsBridges.keySet().forEach(k -> {
            if(!updateList.contains(k)) {
                ovsBridges.remove(k);
            }
        });
    }

    /**
     * 获取ovs网桥列表
     * @return
     */
    @Override
    public List<OvsBridge> getOvsBridges() {
        return new ArrayList<>(ovsBridges.values());
    }

    /**
     * 创建网桥
     */
    public void createBridge(String bridgeName) {
        if(!isInstall) {
            throw new RuntimeException("Ovs未安装！");
        }
        ovsUtils.addBridge(bridgeName);
    }

    public void addPortToBridge(String bridgeName, String port) {
        if(!isInstall) {
            throw new RuntimeException("Ovs未安装！");
        }
        if(ovsBridges.containsKey(bridgeName)) {
            throw new RuntimeException("bridge:" + bridgeName + "不存在！");
        }
        ovsUtils.addBridgePort(bridgeName, port);
    }

    @Override
    public boolean isBridge(String bridgeName) {
        return ovsBridges.containsKey(bridgeName);
    }

    @Override
    public boolean delBridge(String bridgeName) {
        // TODO: 要优化下返回值
        ovsUtils.delBridge(bridgeName);
        return true;
    }

    @Override
    public void addBridge(String bridgeName) {
        // TODO: 优化下返回值
        ovsUtils.addBridge(bridgeName);
        // 这里增加一个时延，防止后端ovs检测不到br
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void setController(String bridgeName, String host, int port) {
        ovsUtils.setController(bridgeName, host, port);
    }

    /**
     * 连接两个虚拟交换机
     * @param br1
     * @param br2
     * @return
     */
    @Override
    public boolean linkOvs(String br1, String br2) {
        // 判断两个Ovs网桥是否存在
        if(!ovsBridges.containsKey(br1) || !ovsBridges.containsKey(br2)) {
            LOG.error("ovs bridge[" + br1, ", " + br2 + "]不存在");
            return false;
        }

        // 先创建虚拟网卡对
        String[] vethPair = ovsUtils.createVethPair(br1, br2);
        // 将虚拟网卡对的两端分别接入到ovs上

        boolean b = ovsUtils.addBridgePort(br1, vethPair[0]);
        boolean b1 = ovsUtils.addBridgePort(br2, vethPair[1]);

        // 启动端口
        boolean b2 = ovsUtils.enableLinuxPort(vethPair);
        return b && b1 && b2;
    }

    @Override
    public boolean delOvs(String br1, String br2) {
        if(!ovsBridges.containsKey(br1) || !ovsBridges.containsKey(br2)) {
            LOG.error("ovs bridge[" + br1, ", " + br2 + "]不存在");
            return false;
        }

        String[] vethPair = {br1 + "_" + br2, br2 + "_" + br1};

        // 删除ovs上的虚拟网卡
        boolean b = ovsUtils.delBridgePort(br1, vethPair[0]);
        boolean b1 = ovsUtils.delBridgePort(br2, vethPair[1]);

        // 删除虚拟网卡对
        boolean b2 = ovsUtils.delVethPort(vethPair);
        return  b1 && b2 && b;
    }

    /**
     * 设置VxLan
     * @param bridgeName
     * @param remoteIp
     * @return
     */
    @Override
    public boolean setVxlan(String bridgeName, String remoteIp) {
        // TODO： 最好再校验一下RemoteIp是否合法
        if (!isBridge(bridgeName)) {
            LOG.warn("bridge[" + bridgeName + "] 不存在");
            return false;
        }
        return ovsUtils.setVxlan(bridgeName, remoteIp);
    }

    // TODO: 这里边的vxlan端口都设置为了vxlan0
    @Override
    public boolean delVxlan(String bridgeName) {
        if(!isBridge(bridgeName)) {
            LOG.warn("bridge[" + bridgeName + "] 不存在");
            return false;
        }
        return ovsUtils.delBridgePort(bridgeName, "vxlan0");
    }

    /**
     * 删除ovs port
     * @param bridgeName
     * @param portName
     * @return
     */
    @Override
    public boolean delBridgePort(String bridgeName, String portName) {
        if(!isBridge(bridgeName)) {
            LOG.warn("bridge[" + bridgeName + "]不存在");
            return false;
        }
        return ovsUtils.delBridgePort(bridgeName, portName);
    }
}
